#include "znx_yaml_parser.h"


znx_bool_t
znx_yaml_map_node_parser(void *conf, znx_pool_t *pool,
    yaml_document_t *doc, yaml_node_t *root,
    znx_command_t *commands, znx_str_t *err)
{
    if (root->type != YAML_MAPPING_NODE) {
        ZNX_STR_SET(err, "this is not a map type configuration file");
        return ZNX_FALSE;
    }

    znx_command_t           *command;
    yaml_node_pair_t        *pair;
    yaml_node_t             *key;
    yaml_node_t             *value;
    size_t                  index;

    for (pair = root->data.mapping.pairs.start;
        pair < root->data.mapping.pairs.top;
        pair++)
    {
        key = yaml_document_get_node(doc, pair->key);
        value = yaml_document_get_node(doc, pair->value);

        if (key->type != YAML_SCALAR_NODE) {
            ZNX_STR_SET(err, "the key node type must is YAML_SCALAR_NODE");
            return ZNX_FALSE;
        }

        index = 0;
        for ( ;; ) {
            command = commands + index;
            index++;

            if (command->name.len == 0) {
                break;
            }

            if (key->data.scalar.length == command->name.len &&
                znx_strncmp(key->data.scalar.value,
                    command->name.data,
                    command->name.len) == 0)
            {
                if (command->set_handle) {
                    if (!command->set_handle(conf, pool, command, doc, value, err)) {
                        return ZNX_FALSE;
                    }
                    break;
                }
            }
        }
    }

    return ZNX_TRUE;
}


znx_bool_t
znx_yaml_map_string_parser(void *conf,
    znx_pool_t *pool, znx_str_t *buffer,
    znx_command_t *commands, znx_str_t *err)
{
    yaml_node_t             *root;
    yaml_parser_t           parser;
    yaml_document_t         doc;

    if (!yaml_parser_initialize(&parser)) {
        ZNX_STR_SET(err, "yaml_parser_initialize() failed");
        return ZNX_FALSE;
    }

    yaml_parser_set_input_string(&parser, buffer->data, buffer->len);

    if (!yaml_parser_load(&parser, &doc)) {
        ZNX_STR_SET(err, "yaml_parser_load() failed");
        yaml_parser_delete(&parser);
        return ZNX_FALSE;
    }

    root = yaml_document_get_root_node(&doc);

    if (!znx_yaml_map_node_parser(conf, pool, &doc, root, commands, err)) {
        yaml_parser_delete(&parser);
        yaml_document_delete(&doc);
        return ZNX_FALSE;
    }

    yaml_parser_delete(&parser);
    yaml_document_delete(&doc);
    return ZNX_TRUE;
}


znx_bool_t
znx_yaml_map_conf_parser(void *conf,
    znx_pool_t *pool, const char *filepath,
    znx_command_t *commands, znx_str_t *err)
{
    znx_str_t               buffer = ZNX_NULL_STRING;
    char                    *err1;
    znx_pool_t              *tmp_pool;

    tmp_pool = znx_pool_create_ext(ZNX_POOL_MIN_PAGE_SIZE);

    if (!znx_file_load(tmp_pool, filepath, &buffer, &err1)) {
        ZNX_STR_SET(err, err1);
        znx_pool_destroy(tmp_pool);
        return ZNX_FALSE;
    }

    if (!znx_yaml_map_string_parser(conf, pool, &buffer, commands, err)) {
        znx_pool_destroy(tmp_pool);
        return ZNX_FALSE;
    }

    znx_pool_destroy(tmp_pool);
    return ZNX_TRUE;
}

znx_bool_t
znx_command_set_int64(
    void *conf, znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    void            *ptr;
    int64_t         *iptr;
    znx_str_t       value;
    size_t          buf_len;

    buf_len = command->name.len + 128;
    ptr = (u_char *)conf + command->offset;
    iptr = (int64_t *)ptr;

    if (node->type != YAML_SCALAR_NODE) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v type is not YAML_SCALAR_NODE", &command->name) - err->data);
        return ZNX_FALSE;
    }

    value.data = (u_char *)node->data.scalar.value;
    value.len = node->data.scalar.length;

    if (!znx_str_to_int64(&value, iptr)) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v to int64 failed", &command->name) - err->data);
        return ZNX_FALSE;
    }

    return ZNX_TRUE;
}


znx_bool_t
znx_command_set_uint64(
    void *conf, znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    void            *ptr;
    int64_t         i = -1;
    uint64_t        *uptr;
    znx_str_t       value;
    size_t          buf_len;

    buf_len = command->name.len + 128;
    ptr = (u_char *)conf + command->offset;
    uptr = (uint64_t *)ptr;

    if (node->type != YAML_SCALAR_NODE) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v type is not YAML_SCALAR_NODE", &command->name) - err->data);
        return ZNX_FALSE;
    }

    value.data = (u_char *)node->data.scalar.value;
    value.len = node->data.scalar.length;

    if (!znx_str_to_int64(&value, &i)) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v to int64 failed", &command->name) - err->data);
        return ZNX_FALSE;
    }

    if (i < 0) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v to uint64_t failed", &command->name) - err->data);
        return ZNX_FALSE;
    }

    *uptr = (uint64_t)i;

    return ZNX_TRUE;
}


znx_bool_t
znx_command_set_sizet(
    void *conf, znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    void            *ptr;
    int64_t         i = -1;
    size_t          *sptr;
    znx_str_t       value;
    size_t          buf_len;

    buf_len = command->name.len + 128;
    ptr = (u_char *)conf + command->offset;
    sptr = (size_t *)ptr;

    if (node->type != YAML_SCALAR_NODE) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v type is not YAML_SCALAR_NODE", &command->name) - err->data);
        return ZNX_FALSE;
    }

    value.data = (u_char *)node->data.scalar.value;
    value.len = node->data.scalar.length;

    if (!znx_str_to_int64(&value, &i)) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v to int64 failed", &command->name) - err->data);
        return ZNX_FALSE;
    }

    if (i < 0) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v to uint64_t failed", &command->name) - err->data);
        return ZNX_FALSE;
    }

    *sptr = (size_t)i;

    return ZNX_TRUE;
}


znx_bool_t
znx_command_set_int(
    void *conf, znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    void            *ptr;
    int64_t         i = -1;
    int             *iptr;
    znx_str_t       value;
    size_t          buf_len;

    buf_len = command->name.len + 128;
    ptr = (u_char *)conf + command->offset;
    iptr = (int *)ptr;

    if (node->type != YAML_SCALAR_NODE) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v type is not YAML_SCALAR_NODE", &command->name) - err->data);
        return ZNX_FALSE;
    }

    value.data = (u_char *)node->data.scalar.value;
    value.len = node->data.scalar.length;

    if (!znx_str_to_int64(&value, &i)) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v to int64 failed", &command->name) - err->data);
        return ZNX_FALSE;
    }

    if (i < 0) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v to uint64_t failed", &command->name) - err->data);
        return ZNX_FALSE;
    }

    *iptr = (int)i;

    return ZNX_TRUE;
}


znx_bool_t
znx_command_set_double(
    void *conf, znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    void            *ptr;
    double         *dptr;
    znx_str_t       value;
    size_t          buf_len;

    buf_len = command->name.len + 128;
    ptr = (u_char *)conf + command->offset;
    dptr = (double *)ptr;

    if (node->type != YAML_SCALAR_NODE) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v type is not YAML_SCALAR_NODE", &command->name) - err->data);
        return ZNX_FALSE;
    }

    value.data = (u_char *)node->data.scalar.value;
    value.len = node->data.scalar.length;

    if (!znx_str_to_double(&value, dptr)) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v to int64 failed", &command->name) - err->data);
        return ZNX_FALSE;
    }

    return ZNX_TRUE;
}


znx_bool_t
znx_command_set_bool(
    void *conf, znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    void            *ptr;
    znx_bool_t      *bptr;
    znx_str_t       value;
    size_t          buf_len;

    buf_len = command->name.len + 128;
    ptr = (u_char *)conf + command->offset;
    bptr = (znx_bool_t *)ptr;

    if (node->type != YAML_SCALAR_NODE) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v type is not YAML_SCALAR_NODE", &command->name) - err->data);
        return ZNX_FALSE;
    }

    value.data = (u_char *)node->data.scalar.value;
    value.len = node->data.scalar.length;

    if (!znx_str_to_bool(&value, bptr)) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v to int64 failed", &command->name) - err->data);
        return ZNX_FALSE;
    }

    return ZNX_TRUE;
}


znx_bool_t
znx_command_set_string(
    void *conf, znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    void            *ptr;
    znx_str_t       *sptr;
    size_t          buf_len;

    buf_len = command->name.len + 128;
    ptr = (u_char *)conf + command->offset;
    sptr = (znx_str_t *)ptr;

    if (node->type != YAML_SCALAR_NODE) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v type is not YAML_SCALAR_NODE", &command->name) - err->data);
        return ZNX_FALSE;
    }

    if (sptr->len != 0) {
        err->data = znx_pool_malloc(pool, buf_len);
        err->len = (size_t)(znx_snprintf(err->data, buf_len,
            "%v is duplicate", &command->name) - err->data);
        return ZNX_FALSE;
    }

    sptr->len = node->data.scalar.length;
    sptr->data = znx_pool_malloc(pool, sptr->len + 1);
    znx_memcpy(sptr->data, node->data.scalar.value, sptr->len);
    sptr->data[sptr->len] = '\0';

    return ZNX_TRUE;
}




